import java.util.List;
import java.util.LinkedList;

public class TinyGA {
   public TinyGA() {
     identado=-1;  
   } 
    
   public interface Sent  {
     public Error err();
     public List<Instruccion> cod();
     public String regla();
   }      
      
   public interface Decs {
     public Error err();
     public TS ts();
     public String regla();
   }
   
   public interface Dec {
     public String cte();
     public String val(); 
     public int fila();
     public int col();
     public String regla();
   }

   abstract public class Exp0  {
     protected Exp0() {
       hay_tsh = false;
     }
     abstract public Error err();
     abstract public List<Instruccion> cod();
     abstract public String regla();
     public TS tsh() {
        if (! hay_tsh) {
          tsh = ctx.tsh();
          hay_tsh= true;
        }
        return tsh;
      }
      public void registraCtx(Exp0Ctx ctx) {
        this.ctx = ctx;
      }
      public Exp0Ctx  contexto() {
	return ctx;
      } 
      private Exp0Ctx ctx;
      private TS tsh;
      private boolean hay_tsh;      
   }
   public interface Exp0Ctx {
     public TS tsh();
     public String regla();
   }
   
   abstract public class Exp1 {
     protected Exp1() {
       hay_tsh = false;
     }
     abstract public Error err();
     abstract public List<Instruccion> cod();
     abstract public String regla();
     public TS tsh() {
        if (! hay_tsh) {
          tsh = ctx.tsh();
          hay_tsh= true;
        }
        return tsh;
      }
      public void registraCtx(Exp1Ctx ctx) {
        this.ctx = ctx;
      }
      public Exp1Ctx contexto() {
	 return ctx;
      }
      private Exp1Ctx ctx;
      private TS tsh;
      private boolean hay_tsh;      
   }
   public interface Exp1Ctx {
     public TS tsh();
     public String regla();
   }
      
     /*
       Sent ::= Decs & Exp0
           Exp0.tsh = Decs.ts
           Sent.err = Decs.err ++ Exp0.err
           Sent.cod = Exp0.cod
     */
   public class SentR1 implements Sent {
      public SentR1(Decs decs, Exp0 exp0) {
         this.decs = decs;
         this.exp0 = exp0;
         this.hay_err = false;
         this.hay_cod = false;
		 
         exp0.registraCtx(
             new Exp0Ctx() {
               public TS tsh() {
                 return SentR1.this.decs.ts();
               }
	      public String regla() {
	         return "Sent ::= Decs & Exp0 | Exp0";
	      }
             });
      }
      public String regla() {
        return "Sent ::= Decs & Exp0 | Sent";   
      }
       
      public Error err() {
         if (! hay_err) {
            err = joinErrors(decs.err(),exp0.err());
            hay_err = true;
         }  
         return err;
      }
      public List<Instruccion> cod() {
         if (! hay_cod) {
            cod = exp0.cod();
            hay_cod = true;
         }		 
         return cod;
      }
      private Decs decs;
      private Exp0 exp0;  
      private List<Instruccion> cod;
      private boolean hay_cod;
      private Error err;
      private boolean hay_err;
   }
   public class SentR1Debug extends SentR1 {
     public SentR1Debug(Decs decs, Exp0 exp0) {super(decs,exp0);}
     public Error err() {requerido(regla()+".err"); return valor(regla()+".err", super.err());}
     public List<Instruccion> cod() {return valor(regla()+".cod=", super.cod());}
   }  
   

     /*
       Decs ::= Decs ; Dec
           Decs(0).ts = aniadeCte(Decs(1).ts,Dec.cte.lex,Dec.val)
           Decs(0).err = Decs(1).err ++ 
		                 errorSi(estaEn(Dec.cte,Decs(1).ts),Dec.fila,Dec.col,"Constante duplicada: "+Dec.cte)
       */
   public class DecsR1 implements Decs {
      public DecsR1(Decs decs_1, Dec dec) {
         this.decs_1 = decs_1;
         this.dec = dec;
         this.hay_ts = false;
         this.hay_err = false;
      }
      public String regla() {
        return "Decs ::= Decs ; Dec | Decs(0)";  
      }
      public TS ts() {
         if (! hay_ts) {
            ts = aniadeCte(decs_1.ts(),dec.cte(),dec.val());
            hay_ts = true;
         }
		 return ts;
      }
      public Error err() {
         if (! hay_err) {
            err = joinErrors(decs_1.err(), errorSi(estaEn(dec.cte(),decs_1.ts()),
			                dec.fila(),dec.col(),"Constante duplicada:"+dec.cte()));
            hay_err = true;
         }
         return err;
      }
      private Decs decs_1;
      private Dec dec;  
      private TS ts;
      private boolean hay_ts;
      private Error err;
      private boolean hay_err;
   }
   public class DecsR1Debug extends DecsR1 {
     public DecsR1Debug(Decs decs_1, Dec dec) {super(decs_1,dec);}
     public Error err() {requerido(regla()+".err"); return valor(regla()+".err", super.err());}
     public TS ts() {requerido(regla()+".ts"); return valor(regla()+".ts", super.ts());}
   }  

     /*
       Decs ::= Dec
           Decs.ts = aniadeCte(creaTS(),Dec.cte,Dec.val)
           Decs.err = noError()
       */
   public class DecsR2 implements Decs {
      public DecsR2(Dec dec) {
         this.dec = dec;
         this.hay_ts = false;
         this.hay_err = false;
      }
      public String regla() {
        return "Decs ::= Dec | Decs";  
      }
      public TS ts() {
         if (! hay_ts) {
            ts = aniadeCte(creaTS(),dec.cte(),dec.val());
            hay_ts = true;
         }
         return ts;
      }
      public Error err() {
         if (! hay_err) {
            err = noError();
            hay_err = true;
         }
         return err;
      }
      private Dec dec;  
      private TS ts;
      private boolean hay_ts;
      private Error err;
      private boolean hay_err;
   }
   public class DecsR2Debug extends DecsR2 {
     public DecsR2Debug(Dec dec) {super(dec);}
     public Error err() {requerido(regla()+".err"); return valor(regla()+".err", super.err());}
     public TS ts() {requerido(regla()+".ts"); return valor(regla()+".ts", super.ts());}
   }  

     /*
       Dec ::= id = num
           Dec.cte = id.lex
           Dec.val = num.lex
		   Dec.fila = id.fila
		   Dec.col = id.col
		   
       */
   public class DecR1 implements Dec {
      public DecR1(Token id, Token num) {
         this.id = id;
         this.num = num;
         this.hay_cte = false;
         this.hay_val = false;
		 this.hay_fila= false;
		 this.hay_col = false;
      }
      public String regla() {
       return "Dec ::= id = num | Dec";   
      }
      public String cte() {
         if (! hay_cte) {
            cte = id.lex();
            hay_cte = true;
         }
         return cte;
      }
      public String val() {
         if (! hay_val) {
            val = num.lex();
            hay_val = true;
         }
         return val;
      }
      public int fila() {
         if (! hay_fila) {
            fila = id.fila();
            hay_fila = true;
         }
         return fila;
      }
      public int col() {
         if (! hay_col) {
            col = id.col();
            hay_col = true;
         }
         return col;
      }
      private Token id;  
      private Token num;  
      private String cte;
      private boolean hay_cte;
      private String val;
      private boolean hay_val;
      private int fila;
      private boolean hay_fila;
      private int col;
      private boolean hay_col;
   }
   public class DecR1Debug extends DecR1 {
     public DecR1Debug(Token id, Token num) {super(id,num);}
     public String cte() {requerido(regla()+".cte"); return valor(regla()+".cte", super.cte());}
     public String val() {requerido(regla()+".val"); return valor(regla()+".val", super.val());}
     public int fila() {requerido(regla()+".fila"); return valor(regla()+".fila", super.fila());}
     public int col() {requerido(regla()+".col"); return valor(regla()+".col", super.col());}
   }   


     /*
       Exp0 ::= Exp0 + Exp1
           Exp0(1).tsh = Exp0(0).tsh
           Exp1.tsh = Exp0(0).tsh
           Exp0(0).err = Exp0(1).err ++ Exp1.err
           Exp0(0).cod = Exp0(0).codd || Exp1.cod || suma
       */
   public class Exp0R1 extends Exp0 {
      public Exp0R1(Exp0 exp0_1, Exp1 exp1) {
         this.exp0_1= exp0_1;
         this.exp1 = exp1;
         this.hay_err = false;
         this.hay_cod = false;
         exp0_1.registraCtx(
            new Exp0Ctx() {
               public TS tsh() {
                  return Exp0R1.this.tsh();
               }
	       public String regla() {
	          return "Exp0 ::= Exp0 + Exp1 | Exp0(1)";
	       }
            });
         exp1.registraCtx(
            new Exp1Ctx() {
               public TS tsh() {
                  return Exp0R1.this.tsh();
               }
 	      public String regla() {
	          return "Exp0 ::= Exp0 + Exp1 | Exp1";
	      }
           });
      }
      public String regla() {
        return "Exp0 ::= Exp0 + Exp1 | Exp0(0)";   
      }
      public Error err() {
         if (! hay_err) {
            err = joinErrors(exp0_1.err(),exp1.err());
            hay_err = true;
         }
         return err;
      }
      public List<Instruccion> cod() {
         if (! hay_cod) {
            cod = concat(exp0_1.cod(),concat(exp1.cod(),suma()));
            hay_cod = true;
         }
         return cod;
      }
      private Exp0 exp0_1;
      private Exp1 exp1;
      private Error err;
      private boolean hay_err;
      private List<Instruccion> cod;
      private boolean hay_cod;
   } 
   public class Exp0R1Debug extends Exp0R1 {
      public Exp0R1Debug(Exp0 exp0_1, Exp1 exp1) {super(exp0_1,exp1);}
      public Error err() {requerido(regla()+".err"); return valor(regla()+".err=", super.err());}
      public List<Instruccion> cod() {requerido(regla()+".cod"); return valor(regla()+".cod=", super.cod());}
      public TS tsh() {requerido(contexto().regla()+".tsh"); return valor(contexto().regla()+".tsh=", super.tsh());}
   }   


     /*
       Exp0 ::= Exp1 * Exp1
           Exp1(0).tsh = Exp0.tsh
           Exp1(1).tsh = Exp0.tsh           
           Exp0.err = Exp1(0).err ++ Exp1(1).err 
           Exp0.cod = Exp1(0).cod || Exp1(1).cod || mul
       */
   public class Exp0R2 extends Exp0 {
      public Exp0R2(Exp1 exp1_0, Exp1 exp1_1) {
         this.exp1_0= exp1_0;
         this.exp1_1= exp1_1;
         this.hay_err = false;
         this.hay_cod = false;
         exp1_0.registraCtx(
            new Exp1Ctx() {
               public TS tsh() {
                  return Exp0R2.this.tsh();
               }
	       public String regla() {
		  return "Exp0 ::= Exp1 * Exp1 | Exp1(0)";
	       }
            });
         exp1_1.registraCtx(
            new Exp1Ctx() {
               public TS tsh() {
                  return Exp0R2.this.tsh();
               }
  	       public String regla() {
		  return "Exp0 ::= Exp1 * Exp1 | Exp1(1)";
	       }
          });
      }
      public String regla() {
	  return "Exp0 ::= Exp1 * Exp1 | Exp0";
      }
      public Error err() {
         if (! hay_err) {
            err = joinErrors(exp1_0.err(),exp1_1.err());
            hay_err = true;
         }
         return err;
      }
      public List<Instruccion> cod() {
         if (! hay_cod) {
            cod = concat(exp1_0.cod(),concat(exp1_1.cod(),mul()));
            hay_cod = true;
         }
         return cod;
      }
      private Exp1 exp1_0;
      private Exp1 exp1_1;
      private Error err;
      private boolean hay_err;
      private List<Instruccion> cod;
      private boolean hay_cod;
   } 
   public class Exp0R2Debug extends Exp0R2 {
      public Exp0R2Debug(Exp1 exp1_0, Exp1 exp1_1) {super(exp1_0,exp1_1);}
      public Error err() {requerido(regla()+".err"); return valor(regla()+".err=", super.err());}
      public List<Instruccion> cod() {requerido(regla()+".cod"); return valor(regla()+".cod=", super.cod());}
      public TS tsh() {requerido(contexto().regla()+".tsh"); return valor(contexto().regla()+".tsh=", super.tsh());}
   }
     /*
       Exp0 ::= Exp1
           Exp1.tsh = Exp0.tsh
           Exp0.err = Exp1.err 
           Exp0.cod = Exp1.cod
       */
   public class Exp0R3 extends Exp0 {
      public Exp0R3(Exp1 exp1) {
         this.exp1 = exp1;
         this.hay_err = false;
         this.hay_cod = false;
         exp1.registraCtx(
            new Exp1Ctx() {
               public TS tsh() {
                  return Exp0R3.this.tsh();
               }
  	       public String regla() {
		  return "Exp0 ::= Exp1 | Exp1";
	       }			   
            });
      }
      public String regla () {
        return "Exp0 ::= Exp1 | Exp0";  
      }
      public Error err() {
         if (! hay_err) {
            err =  exp1.err();
            hay_err = true;
         }
         return err;
      }
      public List<Instruccion> cod() {
         if (! hay_cod) {
            cod = exp1.cod();
            hay_cod = true;
         }
         return cod;
      }
      private Exp1 exp1;
      private Error err;
      private boolean hay_err;
      private List<Instruccion> cod;
      private boolean hay_cod;
   }
   public class Exp0R3Debug extends Exp0R3 {
     public Exp0R3Debug(Exp1 exp1) {super(exp1);}
     public Error err() {requerido(regla()+".err"); return valor(regla()+".err=", super.err());}
     public List<Instruccion> cod() {requerido(regla()+".cod"); return valor(regla()+".cod=", super.cod());}
     public TS tsh() {requerido(contexto().regla()+".tsh"); return valor(contexto().regla()+".tsh=", super.tsh());}
   }   
     /*
       Exp1 ::= num
           Exp1.err = noError() 
           Exp1.cod = apila(num.lex)
       */
   public class Exp1R1 extends Exp1 {
      public Exp1R1(Token num) {
         this.num = num;
         this.hay_err = false;
         this.hay_cod = false;
      }
      public String regla() {
        return "Exp1 ::= num | Exp1";  
      }
      public Error err() {
         if (! hay_err) {
            err =  noError();
            hay_err = true;
         }
         return err;
      }
      public List<Instruccion> cod() {
         if (! hay_cod) {
            cod = single(apila(num.lex()));
            hay_cod = true;
         }
         return cod;
      }
      private Token num;
      private Error err;
      private boolean hay_err;
      private List<Instruccion> cod;
      private boolean hay_cod;
   }
   public class Exp1R1Debug extends Exp1R1 {
     public Exp1R1Debug(Token num) {super(num);}
     public Error err() {requerido(regla()+".err"); return valor(regla()+".err=", super.err());}
     public List<Instruccion> cod() {requerido(regla()+".cod"); return valor(regla()+".cod=", super.cod());}
     public TS tsh() {requerido(contexto().regla()+".tsh"); return valor(contexto().regla()+".tsh=", super.tsh());}
   }   

     /*
       Exp1 ::= id
           Exp1.err = errorSi(not estaEn(id.lex,Exp1.tsh),id.fila,id.col,"Identificador no declarado") 
           Exp1.cod = apila(valDe(id.lex,Exp1.tsh))
       */
   public class Exp1R2 extends Exp1 {
      public Exp1R2(Token id) {
         this.id = id;
         this.hay_err = false;
         this.hay_cod = false;
      }
      public String regla() {
        return "Exp1 ::= id | Exp1";  
      }      
      public Error err() {
         if (! hay_err) {
            err =  errorSi(! estaEn(id.lex(),tsh()),id.fila(),id.col(),"Identificador no declarado:"+id.lex());
            hay_err = true;
         }
         return err;
      }
      public List<Instruccion> cod() {
         if (! hay_cod) {
            cod = single(apila(valDe(id.lex(),tsh())));
            hay_cod = true;
         }
         return cod;
      }
      private Token id;
      private Error err;
      private boolean hay_err;
      private List<Instruccion> cod;
      private boolean hay_cod;
   }
   public class Exp1R2Debug extends Exp1R2 {
     public Exp1R2Debug(Token id) {super(id);}
     public Error err() {requerido(regla()+".err"); return valor(regla()+".err=", super.err());}
     public List<Instruccion> cod() {requerido(regla()+".cod"); return valor(regla()+".cod=", super.cod());}
     public TS tsh() {requerido(contexto().regla()+".tsh"); return valor(contexto().regla()+".tsh=", super.tsh());}
   }   


     /*
       Exp1 ::= (Exp0)
           Exp0.tsh = Exp1.tsh
           Exp1.err = Exp0.err 
           Exp1.cod = Exp0.cod
       */
   public class Exp1R3 extends Exp1 {
      public Exp1R3(Exp0 exp0) {
         this.exp0 = exp0;
         this.hay_err = false;
         this.hay_cod = false;
         exp0.registraCtx(
            new Exp0Ctx() {
               public TS tsh() {
                  return Exp1R3.this.tsh();
               }
               public String regla() {
                  return "Exp1 ::= (Exp0) | Exp0";   
               }
            });
      }
      public String regla() {
        return "Exp1 ::= (Exp0) | Exp1"; 
      }
      public Error err() {
         if (! hay_err) {
            err =  exp0.err();
            hay_err = true;
         }
         return err;
      }
      public List<Instruccion> cod() {
         if (! hay_cod) {
            cod = exp0.cod();
            hay_cod = true;
         }
         return cod;
      }
      private Exp0 exp0;
      private Error err;
      private boolean hay_err;
      private List<Instruccion> cod;
      private boolean hay_cod;
   }
   public class Exp1R3Debug extends Exp1R3 {
     public Exp1R3Debug(Exp0 exp0) {super(exp0);}
     public Error err() {requerido(regla()+".err"); return valor(regla()+".err=", super.err());}
     public List<Instruccion> cod() {requerido(regla()+".cod"); return valor(regla()+".cod=", super.cod());}
     public TS tsh() {requerido(contexto().regla()+".tsh"); return valor(contexto().regla()+".tsh=", super.tsh());}
   }

    
   public TS aniadeCte(TS ts, String cte, String val){
        return ts.aniade(cte,val);         
   } 
   public boolean estaEn(String cte, TS ts){
        return ts.estaEn(cte);         
   } 
   public String valDe(String cte, TS ts){
        return ts.valDe(cte);         
   } 
   public TS creaTS() {
        return new TS();
   }
   public List<Instruccion> concat(List<Instruccion> c1, List<Instruccion> c2) {
       List<Instruccion> result = new LinkedList<Instruccion>(c1);
       result.addAll(c2);
       return result;
   }
   public List<Instruccion> concat(List<Instruccion> c1, Instruccion i) {
      List<Instruccion> result = new LinkedList<Instruccion>(c1);
      result.add(i);
      return result;
   }
   public Instruccion suma() {
       return Instruccion.nuevaISuma();  
   }
   public Instruccion mul() {
       return Instruccion.nuevaIMul();  
   }
   public Instruccion apila(String arg) {
       return Instruccion.nuevaIApila(arg);  
   }
   public List<Instruccion> single(Instruccion i) {
      List<Instruccion> is = new LinkedList<Instruccion>();
      is.add(i);
      return is;
   }
   public Error noError() {
     return new Error();
   }
   public Error joinErrors(Error e1,Error e2) {
     return new Error(e1,e2);
   }
   public Error errorSi(boolean condicion, int fila, int col, String msg) {
     if (condicion) {
	    return new Error("("+fila+","+col+") ERROR CONTEXTUAL: "+msg);
	 }
     else 
	    return new Error();
   }
   
   private void requerido(String ctx) {identado++; identa(); System.out.println("REQUERIDO "+ctx);}
   private <O> O valor(String ctx, O val) {identa(); System.out.println("VALOR DE "+ctx+":"+val); identado--; return val;}
   private void identa() {
     for (int i=0; i < identado; i++) 
         System.out.print(".");   
   }
   
   private int identado;
      
   public static void main(String[] args) {
      TinyGA ga = new TinyGA();
           
      
      TinyGA.Sent sent = ga.new SentR1(
                           ga.new DecsR1(
                             ga.new DecsR2(                                                                      
                               ga.new DecR1(
                                  new Token(CatLexica.ID,"x"),
                                  new Token(CatLexica.NUM,"5"))),
                             ga.new DecR1(
                                new Token(CatLexica.ID,"y"),
                                new Token(CatLexica.NUM,"6"))),
                           ga.new Exp0R1( 
                             ga.new Exp0R3(
                                  ga.new Exp1R2(
                                       new Token(CatLexica.ID,"y"))),
                                  ga.new Exp1R2(
                                       new Token(CatLexica.ID,"x"))));

      
      System.out.println(sent.err());                                           
      System.out.println(sent.cod());                                           
   }   
}
